#ifndef		__ENGINE_NETWORK_H_INCLUDED__
#define		__ENGINE_NETWORK_H_INCLUDED__

#include	<cstdlib>
#include	<string>
#include	<map>

#define		NET_ERR_RUNNING		-1
#define		NET_ERR_ADDR		-2
#define		NET_ERR_TIMEOUT		-3
#define		NET_ERR_CLOSED		-4

#define		NET_CLOSE_LOCAL		0
#define		NET_CLOSE_REMOTE	1
#define		NET_CLOSE_ERROR		2

#define		NET_EVENT_CONNECTED	1
#define		NET_EVENT_CLOSED	2
#define		NET_EVENT_READABLE	3

#define		NET_RECV_PENDING	0
#define		NET_RECV_CLOSED		-1
#define		NET_RECV_ERROR		-2

/**
 * Listener for socket.
 */
struct ISocketListener {
	ISocketListener() {}
	virtual ~ISocketListener() {}
	virtual void OnProcess(int nEvent, int nData) = 0;
};

/**
 * Low level of TCP socket.
 */
struct ISocket {
	ISocketListener * pListener;

	ISocket() : pListener(nullptr) {}
	virtual ~ISocket() {}

	virtual int			Connect(const std::string & sIP, int nPort) = 0;
	virtual int			Listen(const std::string & sIP, int nPort) = 0;
	virtual ISocket *	Accept(uint32_t & nIP) = 0;
	virtual int			Recv(char * pBuf, size_t nSize) = 0;
	virtual bool		Send(const char * pData, size_t nSize) = 0;
	virtual void		Close(int nReason) = 0;
};

/**
 * High level of TCP client side socket.
 */
class IConnector : public ISocketListener {
public:
	IConnector();
	virtual ~IConnector();

	/**
	 * Connect to TCP server.
	 *
	 * \param	sIP		IP address of remote server.
	 * \param	nPort	Port to connect to.
	 * \return Connect status. 0 means successfully, otherwise returns error code.
	 */
	bool Connect(const std::string & sIP, int nPort);

	/**
	 * Close manually.
	 *
	 * \param	nReason	Reason for call this method.
	 */
	void Close(int nReason = NET_CLOSE_LOCAL);

	/**
	 * Send data to remote server.
	 *
	 * \param	pData	Data buffer.
	 * \param	nSize	Size of data in bytes.
	 * \return Send result.
	 */
	bool Send(const char * pData, size_t nSize);

	/**
	 * Process socket readable event.
	 */
	virtual void OnProcess(int nEvent, int nData) override;

	/**
	 * Connected event callback.
	 */
	virtual void OnConnected() {}

	/**
	 * Callback for receive data from server.
	 *
	 * \param	pData	Pointer to received data.
	 * \param	nSize	Size of data in bytes.
	 */
	virtual void OnReceive(const char * pData, size_t nSize) = 0;

	/**
	 * Callback for disconnected event.
	 *
	 * \param	nReason	Close reason.
	 */
	virtual void OnClose(int nReason) {}

private:
	ISocket * _pSocket;
};

/**
 * Each client connected to server side TCP socket.
 */
class IService;
class Connection : public ISocketListener {
public:
	Connection(IService * pOwner, ISocket * pSocket, uint64_t nId, uint32_t nIP);
	~Connection();

	/**
	 * Get unique id of this connect.
	 */
	inline uint64_t Id() const { return _nId; }

	/**
	 * Get remote client IP address.
	 */
	inline const char * IP() const { return _sIP; }

	/**
	 * Attach user data to this connection.
	 *
	 * \param	pUserData	User data to attach to this connect.
	 */
	inline void Attach(void * pUserData) { _pAttached = pUserData; }

	/**
	 * Get attached user data.
	 */
	template<typename T>
	T * Attached() { return _pAttached ? (T*)_pAttached : nullptr; }

	/**
	 * Send data to this client.
	 *
	 * \param	pData	Buffer pointer of data.
	 * \param	nSize	Size of data in bytes.
	 * \return Send status.
	 */
	inline bool Send(const char * pData, size_t nSize) { return _pSocket->Send(pData, nSize); }

	/**
	 * Close this connection manually.
	 *
	 * \param	nReason	Close reason.
	 */
	inline void Close(int nReason = NET_CLOSE_LOCAL) { _pSocket->Close(nReason); }

	/**
	 * Process socket readable event.
	 */
	virtual void OnProcess(int nEvent, int nData) override;

private:
	IService * _pOwner;
	ISocket * _pSocket;
	uint64_t _nId;
	char _sIP[16];
	void * _pAttached;
};

/**
 * High level of TCP server side socket.
 */
class IService : public ISocketListener {
	friend class Connection;

public:
	IService();
	virtual ~IService();

	/**
	 * Listen for connections.
	 *
	 * \param	sIP		Listen address.
	 * \param	nPort	Port to listen on.
	 * \return Listen status. 0 means successfully, otherwise returns error code.
	 */
	int Listen(const std::string & sIP, int nPort);
	
	/**
	 * Find connection by id.
	 */
	Connection * Find(uint64_t nConnId) const;

	/**
	 * Broadcast message to all connected clients.
	 *
	 * \param	pData	Message data to broadcast.
	 * \param	nSize	Size of data in bytes.
	 */
	void Broadcast(const char * pData, size_t nSize);

	/**
	 * Shutdown this server.
	 */
	void Shutdown();

	/**
	 * Process socket readable event.
	 */
	virtual void OnProcess(int nEvent, int nData) override;

	/**
	 * Callback for accept() some client.
	 *
	 * \param	pConn	Connected client information.
	 */
	virtual void OnAccept(Connection * pConn) {}

	/**
	 * Callback for received data from some client.
	 *
	 * \param	pConn	Message comes from.
	 * \param	pData	Received message data.
	 * \param	nSize	Size of data in bytes.
	 */
	virtual void OnReceive(Connection * pConn, const char * pData, size_t nSize) = 0;

	/**
	 * Callback for some client disconnect with this server.
	 *
	 * \param	pConn	Disconnected client.
	 * \param	nReason	Close reason.
	 */
	virtual void OnClose(Connection * pConn, int nReason) {}

	/**
	 * Callback for service shutdown
	 */
	virtual void OnShutdown() {}

private:
	ISocket * _pSocket;
	std::map<uint64_t, Connection *> _mConns;
};

/**
 * Global interfaces.
 */
namespace Network {

	/**
	 * Create platform dependent socket.
	 */
	extern ISocket * CreatePlatformSocket();

	/**
	 * Breath network(Trigger network event loop once).
	 */
	extern void Breath();
}

#endif//!	__ENGINE_NETWORK_H_INCLUDED__